package vn.com.mybudget.base;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.UnmarshallerHandler;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.util.ValidationEventCollector;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import vn.com.mybudget.bo.AccountBO;
import vn.com.mybudget.common.CommonUtil;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import vn.com.mybudget.entities.*;

public class BaseRouter extends BaseController {
	private static final long serialVersionUID = 1069572723508647758L;

	@Override
	public String getServletInfo() {
		return "Welcome to MyBudget!";
	}

	@Override
	protected void processRequest() throws IllegalAccessException,
			IllegalArgumentException, NoSuchMethodException, SecurityException,
			IOException {
		Object output = null;
		// Execute user request
		try {

			// Find execution method
			Method targetMethod = null;
			for (int i = 0; i < this.getClass().getDeclaredMethods().length; i++) {
				if (this.getClass().getDeclaredMethods()[i].getName()
						.equalsIgnoreCase(action)) {
					targetMethod = this.getClass().getDeclaredMethods()[i];
					break;
				}
			}

			// Prepare parameter
			Class<?>[] parameters = targetMethod.getParameterTypes();
			String nameType = "";
			Object[] methodParameterObj = new Object[parameters.length];
			for (int i = 0; i < parameters.length; i++) {
				nameType = targetMethod.getParameterTypes()[i].getName();

				if (nameType != null) {
					if (nameType.contains("vn.com.mybudget.entities")) {
						methodParameterObj[i] = this.GetParameterObject(
								targetMethod.getParameterTypes()[i], i);
					}
					if (nameType.toLowerCase().contains("string")) {
						methodParameterObj[i] = this.GetParameterString(i);
					}
					if (nameType.toLowerCase().contains("float")) {
						methodParameterObj[i] = this.GetParameterFloat(i);
					}
					if (nameType.toLowerCase().contains("int")) {
						methodParameterObj[i] = this.GetParameterInt(i);
					}
				}
			}

			// Temporary support maximum 3 parameter
			if (parameters.length == 0) {
				output = targetMethod.invoke(this);
			} else {
				output = targetMethod.invoke(this, methodParameterObj);
			}

		} catch (InvocationTargetException e) {
			printer.print(e.getMessage());
		} catch (Exception ee) {
			printer.print(ee.getMessage());
		}
		// If method return any outputs(basic type:string, decimal,
		// boolean), write it to client;
		if (output != null) {
			if (output.getClass().getSuperclass() == ViewBase.class) {
				((ViewBase) output).Process(currentRequest, currentResponse);
			} else {
				currentResponse.setContentType("text/plain;charset=UTF-8");
				this.printer.write(output.toString());
			}
		}
	}

	protected Object GetParameterObject(Type obj, int index) throws IOException {
		SchemaFactory sf = SchemaFactory
				.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
		JAXBContext jc;
		Object unmarshalObj = null;
		try {
			db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			InputSource is = new InputSource();
			is.setCharacterStream(new StringReader(this.plainData));
			_plainDataDom = db.parse(is);
			if (_plainDataDom != null) {
				Class c = Class.forName(obj.toString().replace("class", "")
						.trim());
				jc = JAXBContext.newInstance(c.newInstance().getClass());
//				//Schema validate
//				File fSchema = new File(CommonUtil.currentPathContext + "\\WEB-INF\\schema\\"+ "entry.xsd");
//				SchemaFactory schf = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI );
//				Schema schm = sf.newSchema(fSchema);
				Unmarshaller u = jc.createUnmarshaller();
//				u.setSchema(schm);
				unmarshalObj = u.unmarshal(_plainDataDom.getFirstChild()
						.getChildNodes().item(index));
			}
		} catch (JAXBException e) {
			currentResponse.getWriter().println(e.getMessage());
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			currentResponse.getWriter().println(e.getMessage());
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			currentResponse.getWriter().println(e.getMessage());
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			currentResponse.getWriter().println(e.getMessage());
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return unmarshalObj;
	}

	protected String GetParameterString(int index) {
		return GetParameterByIndex(index).toString();
	}

	protected int GetParameterInt(int index) {
		return Integer.parseInt(GetParameterByIndex(index).toString());
	}

	protected float GetParameterFloat(int index) {
		return Float.parseFloat(GetParameterByIndex(index).toString());
	}

	private Document _plainDataDom = null;
	DocumentBuilder db;

	protected Object GetParameterByIndex(int index) {

		try {
			db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			InputSource is = new InputSource();
			is.setCharacterStream(new StringReader(this.plainData));
			_plainDataDom = db.parse(is);
			if (_plainDataDom != null) {
				if (_plainDataDom.getChildNodes().item(index) != null) {
					return _plainDataDom.getFirstChild().getChildNodes()
							.item(index).getTextContent();
				}
			}
		} catch (ParserConfigurationException e) {
			return null;
		} catch (SAXException e) {
			return null;
		} catch (IOException e) {
			return null;
		}
		return null;
	}

	protected boolean ValidateObjectBySchema(Class<?> className, String xml) {
		URL schemaFile;
		try {
			schemaFile = new URL(CommonUtil.currentPathContext + "\\schema\\"
					+ className.getSimpleName()+".xsd");
			File fSchema = new File(CommonUtil.currentPathContext + "\\schema\\"
					+ className.getSimpleName()+".xsd");
			StringReader reader = new StringReader(xml);
			;
			
			SchemaFactory schemaFactory = SchemaFactory
					.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
			Schema schema;
			schema = schemaFactory.newSchema(schemaFile);

			Validator validator = schema.newValidator();

			validator.validate(new javax.xml.transform.stream.StreamSource(reader));
			return true;
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (MalformedURLException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return false;
	}

}
